package top 

import chisel3._
import chisel3.util._
import chisel3.experimental._

import uart2._
import puf2._ 
import puf2.PUF_Constants._
import top.TOP_Constants._

import scala.math._

class control extends Module{
    val io = IO(new Bundle{
        val uart_tx_ready   =   Input(Bool())
        val uart_tx_valid   =   Output(Bool())
        val uart_tx_data    =   Output(UInt(8.W))

        val uart_rx_data    =   Input(UInt(8.W))
        val uart_rx_valid   =   Input(Bool())
        val uart_rx_ready   =   Output(Bool())

        val puf_in_valid    =   Output(Bool())
        val puf_in_ready    =   Input(Bool())
        val puf_data_in     =   Output(UInt(WIDTH_DATA.W))  // !!! attention , to be replaced by the CONSTANT
        
        val puf_out_valid   =   Input(Bool())
        val puf_out_ready   =   Output(Bool())
        val puf_data_out    =   Input(UInt(TRANS_CNT.W))   // !!! attention , to be replaced by the CONSTANT

        val puf_wl_ena      =   Output(Bool())
        val puf_write_ena   =   Output(Bool())

        val led             =   Output(UInt(8.W))
    })

    dontTouch(io.uart_tx_ready)
    dontTouch(io.uart_tx_valid)
    dontTouch(io.uart_tx_data)
    dontTouch(io.uart_rx_data)
    dontTouch(io.uart_rx_valid)
    dontTouch(io.uart_rx_ready)

    val data_control        =   RegInit(0.U(8.W))
    val data_stimulus       =   RegInit(VecInit(Seq.fill( REG_LEN_DATA_IN )(0.U(8.W)))) // Vec( ceil(WIDTH_DATA/8), UInt(8.W) ) // Reg(UInt(80.W))
    val data_output         =   RegInit(VecInit(Seq.fill( REG_LEN_DATA_OUT )(0.U(8.W)))) // Vec( TRANS_CNT/8 , UInt(8.W)) // Reg(UInt(64.W))
    dontTouch(data_control)

    io.led                  :=  data_control

    val cnt_input           =   Reg(UInt(REG_CNT_INPUT_NUM.W))
    val cnt_output          =   Reg(UInt(REG_CNT_OUTPUT_NUM.W))

    val trigger_receive     =   Wire(Bool())
    val trigger_pufin       =   Wire(Bool())
    val trigger_pufout      =   Wire(Bool())
    val trigger_send        =   Wire(Bool())
    val trigger_done        =   Wire(Bool())
    // val trigger_interrupt   =   Wire(Bool())

    val sIDLE :: sRECIVE :: sPUFIN :: sPUFOUT :: sSEND :: Nil = Enum(5) 
    val stateReg    =   RegInit(sIDLE)


//        __   __    __  .___  ___. .______          .___________..______       __    _______   _______  _______ .______      
//       |  | |  |  |  | |   \/   | |   _  \         |           ||   _  \     |  |  /  _____| /  _____||   ____||   _  \     
//       |  | |  |  |  | |  \  /  | |  |_)  |  ______`---|  |----`|  |_)  |    |  | |  |  __  |  |  __  |  |__   |  |_)  |    
// .--.  |  | |  |  |  | |  |\/|  | |   ___/  |______|   |  |     |      /     |  | |  | |_ | |  | |_ | |   __|  |      /     
// |  `--'  | |  `--'  | |  |  |  | |  |                 |  |     |  |\  \----.|  | |  |__| | |  |__| | |  |____ |  |\  \----.
//  \______/   \______/  |__|  |__| | _|                 |__|     | _| `._____||__|  \______|  \______| |_______|| _| `._____|
                                                                                                                           
    when( io.uart_rx_valid && (io.uart_rx_data === "h53".U) ){ // rx_data == 'S'
        trigger_receive     :=  true.B
    }.otherwise{    
        trigger_receive     :=  false.B
    }

    when( cnt_input === UART_CNT_NUM.U ){
        trigger_pufin       :=  true.B 
    }.otherwise{
        trigger_pufin       :=  false.B
    }

    when( io.puf_in_ready ){
        trigger_pufout      :=  true.B
    }.otherwise{
        trigger_pufout     :=  false.B
    }

    when( io.puf_out_valid ){
        trigger_send        :=  true.B 
    }.otherwise{
        trigger_send        :=  false.B
    }

    when( cnt_output === OUT_CNT_NUM.U - 1.U){
        trigger_done        :=  true.B 
    }.otherwise{
        trigger_done        :=  false.B
    }

//      _______.___________.    ___   .___________. _______                __   __    __  .___  ___. .______   
//     /       |           |   /   \  |           ||   ____|              |  | |  |  |  | |   \/   | |   _  \  
//    |   (----`---|  |----`  /  ^  \ `---|  |----`|  |__    ______       |  | |  |  |  | |  \  /  | |  |_)  | 
//     \   \       |  |      /  /_\  \    |  |     |   __|  |______|.--.  |  | |  |  |  | |  |\/|  | |   ___/  
// .----)   |      |  |     /  _____  \   |  |     |  |____         |  `--'  | |  `--'  | |  |  |  | |  |      
// |_______/       |__|    /__/     \__\  |__|     |_______|         \______/   \______/  |__|  |__| | _|      

    when( stateReg === sIDLE ){
        when( trigger_receive ){
            stateReg    :=  sRECIVE
        }
    }
    .elsewhen( stateReg === sRECIVE ){
        when( trigger_pufin ){
            stateReg    :=  sPUFIN
        }
    }
    .elsewhen( stateReg === sPUFIN ){
        when( trigger_pufout ){
            stateReg    :=  sPUFOUT
        }
    }
    .elsewhen( stateReg === sPUFOUT ){
        when( trigger_send ){
            stateReg    :=  sSEND
        }
    }
    .elsewhen( stateReg === sSEND ){
        when( trigger_done ){
            stateReg    :=  sIDLE
        }
    }

//   ______    __    __  .___________..______    __    __  .___________.
//  /  __  \  |  |  |  | |           ||   _  \  |  |  |  | |           |
// |  |  |  | |  |  |  | `---|  |----`|  |_)  | |  |  |  | `---|  |----`
// |  |  |  | |  |  |  |     |  |     |   ___/  |  |  |  |     |  |     
// |  `--'  | |  `--'  |     |  |     |  |      |  `--'  |     |  |     
//  \______/   \______/      |__|     | _|       \______/      |__|     

    io.uart_rx_ready    :=  true.B 

    when( stateReg === sRECIVE ){
        when( io.uart_rx_valid ){
            when( cnt_input < REG_LEN_DATA_IN.U ){
                data_stimulus( cnt_input )      :=  io.uart_rx_data
            }
            .elsewhen( cnt_input === REG_LEN_DATA_IN.U ){
                data_control    :=  io.uart_rx_data
            }
            
            cnt_input                       :=  cnt_input + 1.U
        }
    }.otherwise{
        cnt_input   :=  0.U
    }


    when( stateReg === sPUFIN ){
        io.puf_in_valid :=  true.B 
        // !!! attention
        // io.puf_data_in  :=  Cat( data_stimulus(7)(3, 0) , data_stimulus(6) , data_stimulus(5) , data_stimulus(4) , data_stimulus(3) , data_stimulus(2) , data_stimulus(1) , data_stimulus(0) )
        io.puf_data_in  :=  data_stimulus.asUInt
    }.otherwise{
        io.puf_in_valid :=  false.B 
        io.puf_data_in  :=  0.U
    }

    // val the_puf_wl_ena      =   RegInit(false.B)
    // val the_puf_write_ena   =   RegInit(false.B)

    // io.puf_wl_ena           :=  the_puf_wl_ena
    // io.puf_write_ena        :=  the_puf_write_ena

    when( stateReg === sPUFOUT ){
        io.puf_out_ready    := true.B

        // the_puf_wl_ena          :=  data_control(3,0) === "b1111".U 
        // the_puf_write_ena       :=  data_control(7,4) === "b1111".U 
        io.puf_wl_ena           :=  data_control(3,0) === "b1111".U 
        io.puf_write_ena        :=  data_control(7,4) === "b1111".U 
        
        when( io.puf_out_valid ){
            for(i <- 0 until REG_LEN_DATA_OUT){ 
                data_output(i) := io.puf_data_out( (i+1)*8 -1 , i*8)
            }
            // data_output(0)     := io.puf_data_out( 7, 0)
            // data_output(1)     := io.puf_data_out(15, 8)
            // data_output(2)     := io.puf_data_out(23, 16)
            // data_output(3)     := io.puf_data_out(31, 24)
            // data_output(4)     := io.puf_data_out(39, 32)
            // data_output(5)     := io.puf_data_out(47, 40)
            // data_output(6)     := io.puf_data_out(55, 48)
            // data_output(7)     := io.puf_data_out(63, 56)
        }
    }.otherwise{
        io.puf_out_ready    := false.B 

        io.puf_wl_ena       :=  false.B 
        io.puf_write_ena    :=  false.B 
    }

    when( stateReg === sSEND ){
        io.uart_tx_valid    :=  true.B 
        when( io.uart_tx_ready ){
            io.uart_tx_data :=  data_output(cnt_output)
            cnt_output      :=  cnt_output + 1.U
        }.otherwise{
            io.uart_tx_data :=  0.U
        }
    }.otherwise{
        io.uart_tx_data     :=  0.U
        io.uart_tx_valid    :=  false.B 
        cnt_output          :=  0.U
    }


}
